Technotes
T E C H N O T E : Inside Macintosh: Processes: Time Manager Addenda
This Note highlights the usage of two fields -- tmReserved and tmWakeUp -- that might be unclear after reading the chapter "Time Manager" in Inside Macintosh: Processes. This Note is intended for all developers who want to do time measurement using the Time Manager routines.Setting Up tmReservedOn page 3-8 of Inside Macintosh: Processes, it clearly states that both tmWakeUp and tmReserved should be set to 0 prior to the first call to InsXTime when using the extended Time Manager:theTMTask.tmWakeUp = theTMTask.tmReserved = 0; InsXTime((QElemPtr)&theTMTask); PrimeTime((QElemPtr)&theTMTask, 2000);If you do want to do some time measurement, then you have to call RmvTime to get the current value of tmCount, which leads later to a new call to InsXTime, and a call to PrimeTime with a 0 delay which has a special meaning in that case. Although it appears, after much reading, rather clear that you leave the current value of tmWakeUp untouched in the TMTask structure, you can't be sure what to do about the value of tmReserved. The truth is that prior to October, 1992 (System Software 7.1), you didn't care, but it's more of a concern now, since Apple slightly modified the behavior of the Time Manager to deal with performance issues. If you leave tmReserved untouched, then, after 127 calls to the following code: RmvTime((QElemPtr)&theTMTask); remaining = theTMTask.tmCount; InsXTime((QElemPtr)&theTMTask); PrimeTime((QElemPtr)&theTMTask, 0);for some good but can't-be-disclosed reason, your extended time task is converted into a non-extended time task which, being waked up with a 0 delay PrimeTime (which has no special meaning for a non-extended time task), will suddenly be called and called again -- more frequently than it should be. So, if you perform that kind of time measurement, be sure to write instead: RmvTime((QElemPtr)&theTMTask); remaining = theTMTask.tmCount; theTMTask.tmReserved = 0; InsXTime((QElemPtr)&theTMTask); PrimeTime((QElemPtr)&theTMTask, 0);Since the Time Manager, prior to System Software 7.1, doesn't care about tmReserved, then you can set tmReserved to 0 before each call to InsXTime without checking the system version. You still have, of course, to ensure that the Time Manager you're using is the extended one (gestaltTimeMgrVersion 'tmgr', answering gestaltExtendedTimeMgr (= 3)), and, at this point, there is no way to tell what's going to happen under Mac OS 8. About tmWakeUpThe following sentence, also on page 3-8 in Inside Macintosh: Processes, is incorrect: "The tmWakeUp field contains the time at which the Time Manager task specified by tmAddr was last executed (or 0 if it has not yet been executed)." It should say: "The tmWakeUp field contains the time at which the Time Manager task specified by tmAddr is scheduled to be executed (or 0 if it has not yet been executed)."
Undeferred Time Manager TasksThis section describes an optimization that you might want to employ when using the Time Manager in the presence of virtual memory (VM). Most developers will not be interested in this; however, all users of the Time Manager should heed the following warning.
As described in Inside Macintosh: Memory, Time Manager tasks are automatically deferred by the Virtual Memory (VM) system to avoid double page faults. This was done for backward compatibility with existing applications that use the Time Manager, but it can seriously increase the latency between when the timer expires and when your Time Manager task executes. For more information about interactions between the Time Manager and VM, see Technote 1094, "Virtual Memory Application Compatibility". For example, if you set a Time Manager task to execute at time X and, at time (X - delta) some process takes a page fault, your Time Manager task will not be called until time (X + Y - delta), where Y is the time required to field a page fault. If the page fault causes the hard disk to seek, Y could be as great as the hard disk's average seek time, approximately 10ms. If you are trying to use the Time Manager to measure time in microseconds, this could be a problem. There is a way you can install Time Manager tasks so the callback is not deferred by VM; however, before using this technique, you should be aware of the dangers. Because VM does not defer these special Time Manager tasks, it is possible for them to fire when paging is not safe. To avoid fatal page faults, you must ensure:
HoldMemory(&theTask, sizeof(TMTask));
// Ensure the code doesn't move in logical memory HLock(ttaskCodeHandle); // Ensure the code is held in physical memory and cannot be paged to disk HoldMemory(*ttaskCodeHandle, GetResourceSizeOnDisk(ttaskCodeHandle));
If you call InsTime or InsXTime with the qLink field set to $65616461, the VM patch on the Time Manager will recognize your special requirements and execute your timer task as soon as it fires, regardless of whether paging is safe or not. Another Way to Perform Time MeasurementAnother way to perform time measurement would be to use the Microseconds call, which is much easier to use and less likely to change in the future:pascal void Microseconds(UnsignedWide *microseconds);
SummaryThe following points explain what you should and should not do in working with the Time Manager:
Further ReferenceTechnotes Previous Technote | Contents | Next Technote |